﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Sinacor.Infra.Core.ProcessManager.Server;
using PSE.ProcessManager.Common.Process;
using Sinacor.Infra.Service.Validation;
using PSE.Framework.Communication.Client.File.XmlFile;
using PSE.Framework.Communication.Configuration.File.XmlFile;
using PSE.Framework.Communication.Configuration;
using System.IO;
using PSE.Framework.Communication.Adapters.File;
using Sinacor.Infra.Common.Exceptions;
using Sinacor.Infra.Service.ExceptionHandling;

namespace Sinacor.Process.Bovespa.Financeiro.Server.ProcessLogic
{
    public class Exportacao : BaseProcessLogic
    {
        #region Atributos

        private int _erroDataPregaoInvalida = 109883;
        private int _erroClienteNaoFilhoteBroker = 109884;
        private int _erroClienteNaoCadastrado = 109885;
        private int _erroBrokerNaoCadastrado = 109886;
        private int _erroBrokerNaoInformado = 109887;
        private int _erroClienteNaoPossuiMovimento = 109888;
        private int _erroGeralExportacaoArquivo = 109889;
        private int _erroExportacaoSemRegistros = 109890;

        #endregion

        #region Eventos

        public event EventHandler ExportacaoConcluida;

        #endregion

        #region Propriedades

        public int ErroDataPregaoInvalida
        {
            get { return _erroDataPregaoInvalida; }
            set { _erroDataPregaoInvalida = value; }
        }

        public int ErroClienteNaoFilhoteBroker
        {
            get { return _erroClienteNaoFilhoteBroker; }
            set { _erroClienteNaoFilhoteBroker = value; }
        }

        public int ErroClienteNaoCadastrado
        {
            get { return _erroClienteNaoCadastrado; }
            set { _erroClienteNaoCadastrado = value; }
        }

        public int ErroBrokerNaoCadastrado
        {
            get { return _erroBrokerNaoCadastrado; }
            set { _erroBrokerNaoCadastrado = value; }
        }

        public int ErroBrokerNaoInformado
        {
            get { return _erroBrokerNaoInformado; }
            set { _erroBrokerNaoInformado = value; }
        }

        public int ErroClienteNaoPossuiMovimento
        {
            get { return _erroClienteNaoPossuiMovimento; }
            set { _erroClienteNaoPossuiMovimento = value; }
        }

        public int ErroGeralExportacaoArquivo
        {
            get { return _erroGeralExportacaoArquivo; }
            set { _erroGeralExportacaoArquivo = value; }
        }

        public int ErroExportacaoSemRegistros
        {
            get { return _erroExportacaoSemRegistros; }
            set { _erroExportacaoSemRegistros = value; }
        }

        #endregion

        #region Construtor

        /// <summary>
        /// Construtor padrão
        /// </summary>
        public Exportacao()
        {
        }

        #endregion

        #region Métodos Públicos

        public void ExportarFinanceiro(ProcessEntity.Parameter.FiltroExportacaoFinanceiro entity)
        {
            bool realizarMontagemFinanceiroAnaliticoFAX = false;
            ProcessEntity.Resultado resultado = null;
            ProcessEntity.Cliente broker;
            ProcessEntity.Cliente cliente;
            ProcessEntity.Parameter.FiltroMontagemFAX montagemFAX;
            List<ProcessEntity.Parameter.FiltroMontagemFAX> listaMontagemFAX;
            List<ProcessEntity.Calendario> calendario;
            List<ProcessEntity.Financeiro> listaExportacao;
            ValidationException validationException;
            Communication.Cliente communicationCliente;
            Communication.CalendarioOrdens communicationCalendarioOrdens;
            ResourceAccess.Exportacao raExportacao;
            SinacorValidation validation = new SinacorValidation();

            validation.Validate<ProcessEntity.Parameter.FiltroExportacaoFinanceiro>(entity);
            raExportacao = new ResourceAccess.Exportacao();

            if (entity.TipoFormatoFinanceiro == ProcessEntity.Enum.TipoFormatoFinanceiro.Broker)
            {
                validationException = new ValidationException();
                communicationCliente = new Communication.Cliente();

                if (!entity.CodigoBroker.HasValue
                    || entity.CodigoBroker.Value <= 0)
                    throw new BusinessException(ErroBrokerNaoInformado);

                broker = communicationCliente.ObterCliente(new ProcessEntity.Parameter.FiltroCliente { CodigoCliente = entity.CodigoBroker });

                if (broker == null)
                    validationException.AddError(ErroBrokerNaoCadastrado);

                if (entity.CodigoCliente.HasValue
                    && entity.CodigoCliente > 0)
                {
                    cliente = communicationCliente.ObterCliente(new ProcessEntity.Parameter.FiltroCliente { CodigoCliente = entity.CodigoCliente });

                    if (cliente == null)
                    {
                        validationException.AddError(ErroClienteNaoCadastrado);
                    }
                    else
                    {
                        if (cliente.Broker != entity.CodigoBroker)
                        {
                            validationException.AddError(ErroClienteNaoFilhoteBroker);
                        }
                        else if (!raExportacao.VerificarMovimentoCliente(entity))
                        {
                            throw new BusinessException(ErroClienteNaoPossuiMovimento);
                        }
                    }
                }

                if (validationException.ValidationErrors.Count > 0)
                    throw validationException;
            }

            communicationCalendarioOrdens = new Communication.CalendarioOrdens();

            calendario = communicationCalendarioOrdens.ConsultarCalendarios(
                new ProcessEntity.Parameter.FiltroCalendario
                {
                    DataPregao = entity.DataPregao.Value
                });

            if (calendario == null)
                throw new BusinessException(ErroDataPregaoInvalida);

            if (entity.TipoFormatoFinanceiro == ProcessEntity.Enum.TipoFormatoFinanceiro.Broker)
            {
                if (!raExportacao.VerificarMovimentoBrokers(entity))
                {
                    realizarMontagemFinanceiroAnaliticoFAX = true;
                    entity.DataMontagemAnaliticoFAX = DateTime.Now.ToString("yyyyMMddHHmmss");

                    montagemFAX = new ProcessEntity.Parameter.FiltroMontagemFAX();
                    montagemFAX.CodigoBroker = entity.CodigoBroker.Value;
                    montagemFAX.DataMontagemAnaliticoFAX = entity.DataMontagemAnaliticoFAX;
                    montagemFAX.DataPregao = entity.DataPregao.Value;
                    montagemFAX.IndicadorBroker = "B";
                    montagemFAX.MaquinaUsuario = entity.MaquinaUsuario;

                    resultado = raExportacao.RealizarMontagemFinanceiroAnaliticoFAX(montagemFAX);
                }
            }
            else
            {
                if (!raExportacao.VerificarMovimentoFechado())
                {
                    realizarMontagemFinanceiroAnaliticoFAX = true;
                    entity.DataMontagemAnaliticoFAX = DateTime.Now.ToString("yyyyMMddHHmmss");

                    listaMontagemFAX = raExportacao.ConsultarBrokersPorClube(entity);

                    foreach (ProcessEntity.Parameter.FiltroMontagemFAX item in listaMontagemFAX)
                    {
                        item.DataMontagemAnaliticoFAX = entity.DataMontagemAnaliticoFAX;
                        item.DataPregao = entity.DataPregao.Value;
                        item.MaquinaUsuario = entity.MaquinaUsuario;

                        resultado = raExportacao.RealizarMontagemFinanceiroAnaliticoFAX(item);

                        if (!resultado.Sucesso)
                        {
                            break;
                        }
                    }
                }
            }

            if (resultado != null
                && !resultado.Sucesso)
            {
                TratarExcecoes(resultado);
            }

            if (realizarMontagemFinanceiroAnaliticoFAX)
            {
                listaExportacao = raExportacao.ConsultarFinanceiroPosMontagemFinanceiroAnaliticoFAX(entity);
            }
            else
            {
                listaExportacao = raExportacao.ConsultarFinanceiro(entity);
            }

            if (listaExportacao == null
                || listaExportacao.Count == 0)
            {
                throw new BusinessException(this.ErroExportacaoSemRegistros);
            }

            ExportarXML(entity, listaExportacao, raExportacao.ObterCodigoEmpresaBolsa());

            if (ExportacaoConcluida != null)
                ExportacaoConcluida(this, new EventArgs());
        }

        #endregion

        #region Métodos Privados

        private void ExportarXML(ProcessEntity.Parameter.FiltroExportacaoFinanceiro entity, List<ProcessEntity.Financeiro> listaExportacao, int codigoEmpresaBolsa)
        {
            XmlFileOutputConfiguration config;
            FileInfo fi;
            Layout.FinanceiroXMLIdentificador financeiroXMLIdentificador = null;
            Layout.FinanceiroXMLEvento financeiroXMLEvento = null;
            List<Layout.FinanceiroXMLNegocio> financeiroXMLNegocio = null;
            Translator.ProcessEntityToLayout.Financeiro translatorLayout;
            int registroAtual = 0;
            decimal porcentagemPorRegistro = 0;
            List<int> listaCodigosCliente = new List<int>();

            porcentagemPorRegistro = (listaExportacao.Count > 0)
                                                             ? 100M / (decimal)listaExportacao.Count
                                                             : 1;

            using (XmlFileOutputClient clientXML = new XmlFileOutputClient())
            {
                try
                {
                    clientXML.Open("ExportacaoFINAXML");
                    config = CommunicationConfigurationManager.GetInstance().GetCommunicationClientConfiguration("ExportacaoFINAXML") as XmlFileOutputConfiguration;
                    fi = new FileInfo(entity.NomeArquivo);
                    clientXML.OpenFile(new FileOpenParameter(config == null
                                                                      ? null
                                                                      : config.OutputPath,
                                                             fi.Name));

                    translatorLayout = new Translator.ProcessEntityToLayout.Financeiro();

                    financeiroXMLIdentificador = new Layout.FinanceiroXMLIdentificador
                    {
                        CodigoArquivo = "FINA",
                        CodigoUsuario = codigoEmpresaBolsa,
                        DataGeracao = DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"),
                    };

                    financeiroXMLIdentificador.Evento = new List<Layout.FinanceiroXMLEvento>();

                    foreach (ProcessEntity.Financeiro item in listaExportacao)
                    {
                        if (IsAborted)
                            break;

                        if (!listaCodigosCliente.Contains(item.CodigoCliente))
                        {
                            listaCodigosCliente.Add(item.CodigoCliente);

                            financeiroXMLNegocio = new List<Layout.FinanceiroXMLNegocio>();

                            financeiroXMLEvento = new Layout.FinanceiroXMLEvento
                            {
                                CodigoCliente = item.CodigoCliente,
                                DataNegocio = entity.DataPregao.Value.ToString("dd/MM/yyyy"),
                                Negocio = financeiroXMLNegocio
                            };

                            financeiroXMLEvento.Negocio = financeiroXMLNegocio;

                            financeiroXMLIdentificador.Evento.Add(financeiroXMLEvento);
                        }

                        financeiroXMLNegocio.Add(translatorLayout.ToFinanceiroXMLNegocio(item));

                        registroAtual++;

                        SendStatus(Convert.ToInt32(Math.Floor(porcentagemPorRegistro * registroAtual)), "Bovespa.ImportacaoExportacao.Message.ExportandoArquivo");
                    }

                    clientXML.WriteRecord(financeiroXMLIdentificador, "FinanceiroXMLIdentificador");

                    clientXML.CloseFile();
                }
                catch
                {
                    if (clientXML.Status == PSE.Framework.Communication.CommunicationStatus.Opened)
                        clientXML.CancelFile();

                    throw;
                }
            }
        }

        private void TratarExcecoes(ProcessEntity.Resultado request)
        {
            List<Object> _params;

            if (request.CodigoErro == -1)
            {
                throw new BusinessException(this.ErroGeralExportacaoArquivo);
            }
            else
            {
                if (!String.IsNullOrEmpty(request.Argumentos))
                {
                    _params = ErrorMessage.ConvertXmlArgumentsToParameters(request.Argumentos);

                    throw new BusinessException(request.CodigoErro, _params.ToArray());
                }
                else
                {
                    throw new BusinessException(request.CodigoErro);
                }
            }
        }

        private void AtualizaStatus(int progress, string resourceKey)
        {
            this.Status.Progress = progress;
            this.Status.Step = resourceKey;
            this.SendStatus();
        }

        #endregion
    }
}
